﻿using System;
using System.Collections.Generic;

namespace Common.Date
{
    /// <summary>
    /// Ermitteln von Feiertagen
    /// Quelle:
    /// http://dotnet-snippets.de/dns/ermittlung-von-feiertagen-feiertaglogic-SID763.aspx
    /// </summary>
    public class HollydayLogic
    {
        #region Static
        private static HollydayLogic Instance;
        public static HollydayLogic GetInstance(int year)
        {
            if (Instance == null || year != Instance.CurrentYear)
            {
                Instance = new HollydayLogic(year);
                return Instance;
            }
            return Instance;
        }
        #endregion

        #region Member
        private List<Hollydays> feiertage;
        private int year;
        #endregion

        #region Properties
        public int CurrentYear
        {
            get { return year; }
            set { year = value; }
        }

        /// <summary>
        /// Gibt eine Liste aus variablen Feiertage zurueck
        /// </summary>
        public List<Hollydays> VariableFeiertage
        {
            get
            {
                return feiertage.FindAll(delegate(Hollydays f) { return !f.IsFix; });
            }
        }

        /// <summary>
        /// gibt eine Liste aus festen Feiertagen zurueck
        /// </summary>
        public List<Hollydays> FesteFeiertage
        {
            get
            {
                return feiertage.FindAll(delegate(Hollydays f) { return f.IsFix; });
            }
        }
        #endregion

        #region Public Functions
        /// <summary>
        /// Prüft ob das übergebene Datum ein Feiertag ist.
        /// </summary>
        /// <param name="value">Datum was überprüft werden soll</param>
        /// <returns>TRUE->Ist Feietag | FALSE->Kein Feiertag</returns>
        public bool IsFeiertag(DateTime value)
        {
            return (feiertage.Find(delegate(Hollydays f) { return f.Datum.Date == value.Date; }) != null);
        }

        /// <summary>
        /// Ermitteln des Feiertagsnamen für das übergebene Datum
        /// </summary>
        public Hollydays GetFeiertagName(DateTime value)
        {
            return (feiertage.Find(delegate(Hollydays f) { return f.Datum.Date == value.Date; }));
        }
        #endregion

        #region Private Functions
        /// <summary>
        /// Berechnung der Einzelnen Feiertage für das Jahr.
        /// </summary>
        private HollydayLogic(int year)
        {
            //Sind nur die Feiertage gesetzt die auch in Sachsen gültig sind.
            this.CurrentYear = year;
            this.feiertage = new List<Hollydays>();
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 1, 1), "Neujahr"));
            //this.feiertage.Add(new Hollydays(true, new DateTime(year, 1, 6), "Heilige Drei Könige"));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 5, 1), "Tag der Arbeit"));
            //this.feiertage.Add(new Hollydays(true, new DateTime(year, 8, 15), "Mariä Himmelfahrt"));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 10, 3), "Tag der dt. Einheit"));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 10, 31), "Reformationstag"));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 11, 16), "Buß und Bettag"));
            //this.feiertage.Add(new Hollydays(true, new DateTime(year, 11, 1), "Allerheiligen "));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 12, 25), "1. Weihnachtstag"));
            this.feiertage.Add(new Hollydays(true, new DateTime(year, 12, 26), "2. Weihnachtstag"));

            DateTime osterSonntag = GetOsterSonntag();
            this.feiertage.Add(new Hollydays(false, osterSonntag, "Ostersonntag"));
            //this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(-3), "Gründonnerstag"));
            this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(-2), "Karfreitag"));
            this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(1), "Ostermontag"));
            this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(39), "Christi Himmelfahrt"));
            this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(49), "Pfingstsonntag"));
            this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(50), "Pfingstmontag"));
            //this.feiertage.Add(new Hollydays(false, osterSonntag.AddDays(60), "Fronleichnam"));
        }

        /// <summary>
        /// Berechnung des Oserstonntags, von dem Weitere Feiertaga abhängen.
        /// </summary>
        private DateTime GetOsterSonntag()
        {
            int g, h, c, j, l, i;

            g = year % 19;
            c = this.year / 100;
            h = ((c - (c / 4)) - (((8 * c) + 13) / 25) + (19 * g) + 15) % 30;
            i = h - (h / 28) * (1 - (29 / (h + 1)) * ((21 - g) / 11));
            j = (year + (year / 4) + i + 2 - c + (c / 4)) % 7;

            l = i - j;
            int month = (int)(3 + ((l + 40) / 44));
            int day = (int)(l + 28 - 31 * (month / 4));

            return new DateTime(year, month, day);
        }
        #endregion
    }
}
